package com.judge.config;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import com.common.core.constants.JudgeConstants;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.ListImagesCmd;
import com.github.dockerjava.api.command.PullImageCmd;
import com.github.dockerjava.api.command.PullImageResultCallback;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Container;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.Image;
import com.github.dockerjava.api.model.Volume;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

@Slf4j
public class DockerSandBoxPool {

    private DockerClient dockerClient;

    private String sandboxImage;

    private String volumeDir;

    private Long memoryLimit;

    private Long memorySwapLimit;

    private Long cpuLimit;

    private int poolSize;

    private String containerNamePrefix;

    private BlockingQueue<String> containerQueue;

    private Map<String, String> containerNameMap;

    public DockerSandBoxPool(DockerClient dockerClient,
                             String sandboxImage,
                             String volumeDir, Long memoryLimit,
                             Long memorySwapLimit, Long cpuLimit,
                             int poolSize, String containerNamePrefix) {
        this.dockerClient = dockerClient;
        this.sandboxImage = sandboxImage;
        this.volumeDir = volumeDir;
        this.memoryLimit = memoryLimit;
        this.memorySwapLimit = memorySwapLimit;
        this.cpuLimit = cpuLimit;
        this.poolSize = poolSize;
        this.containerQueue = new ArrayBlockingQueue<>(poolSize);
        this.containerNamePrefix = containerNamePrefix;
        this.containerNameMap = new HashMap<>();
    }

    public void initDockerPool() {  //初始化容器池的
        log.info("开始创建容器池，数量: {}", poolSize);
        log.info("容器池配置 - 池大小: {}, 镜像: {}, 挂载目录: {}", poolSize, sandboxImage, volumeDir);
        for(int i = 0; i < poolSize; i++) {
            String containerName = containerNamePrefix + "-" + i;
            log.info("创建容器: {}", containerName);
            createContainer(containerName);
        }
        log.info("容器池创建完成");
        log.info("容器池初始化完成，可用容器数: {}", containerQueue.size());
        
        // 验证容器池状态
        if (containerQueue.isEmpty()) {
            log.error("容器池初始化失败！没有可用容器");
            throw new RuntimeException("容器池初始化失败，没有可用容器");
        }
        
        // 打印容器池详细信息
        log.info("容器池详细信息:");
        log.info("- 容器名称映射: {}", containerNameMap);
        log.info("- 队列大小: {}", containerQueue.size());
    }

    public String getContainer() {
        try {
            log.info("容器池状态 - 可用容器数: {}, 总容量: {}", containerQueue.size(), poolSize);
            String containerId = containerQueue.take();
            log.info("获取到容器ID: {}", containerId);
            return containerId;
        } catch (InterruptedException e) {
            log.error("获取容器被中断: {}", e.getMessage(), e);
            throw new RuntimeException("获取容器被中断", e);
        }
    }

    public void returnContainer(String containerId) {
        containerQueue.add(containerId);
    }

    public DockerClient getDockerClient() {
        return dockerClient;
    }

    private void createContainer(String containerName) {
        try {
            log.info("开始创建容器: {}", containerName);
            List<Container> containerList = dockerClient.listContainersCmd().withShowAll(true).exec();
            if (!CollectionUtil.isEmpty(containerList)) {
                log.info("检查现有容器，查找名称: {}", containerName);
                
                for (Container container : containerList) {
                    String[] containerNames = container.getNames();
                    if (containerNames != null && containerNames.length > 0) {
                        // 检查容器名称是否匹配（去掉前缀/）
                        String actualName = containerNames[0].startsWith("/") ? containerNames[0].substring(1) : containerNames[0];
                        log.info("检查容器: {} -> 实际名称: {}", containerNames[0], actualName);
                        if (containerName.equals(actualName)) {
                            log.info("找到现有容器: {} (状态: {})", container.getId(), container.getState());
                            if ("created".equals(container.getState()) || "exited".equals(container.getState())) {
                                //启动容器
                                dockerClient.startContainerCmd(container.getId()).exec();
                                log.info("容器已启动: {}", container.getId());
                            }
                            containerQueue.add(container.getId());
                            containerNameMap.put(container.getId(), containerName);
                            log.info("容器已添加到池中: {}", container.getId());
                            return;
                        }
                    }
                }
            }

            //拉取镜像
            log.info("拉取镜像: {}", JudgeConstants.JAVA_ENV_IMAGE);
            pullJavaEnvImage();
            
            //创建容器  限制资源   控制权限
            log.info("创建新容器: {}", containerName);
            HostConfig hostConfig = getHostConfig(containerName);
            CreateContainerCmd containerCmd = dockerClient
                    .createContainerCmd(JudgeConstants.JAVA_ENV_IMAGE)
                    .withName(containerName)
                    .withCmd("sleep", "infinity") // 让容器保持运行状态
                    .withUser("root") // 使用root用户避免权限问题
                    .withEnv("HOME=/root", "USER=root"); // 设置用户环境变量
            
            CreateContainerResponse createContainerResponse = containerCmd
                    .withHostConfig(hostConfig)
                    .withAttachStderr(true)
                    .withAttachStdout(true)
                    .withTty(true)
                    .exec();
            //记录容器id
            String containerId = createContainerResponse.getId();
            log.info("容器创建成功: {}", containerId);
            
            //启动容器
            dockerClient.startContainerCmd(containerId).exec();
            log.info("容器启动成功: {}", containerId);
            
            // 添加到容器池
            containerQueue.add(containerId);
            containerNameMap.put(containerId, containerName);
            log.info("新容器已添加到池中: {}", containerId);
            
        } catch (Exception e) {
            log.error("创建容器失败: {}", containerName, e);
            throw new RuntimeException("创建容器失败: " + containerName, e);
        }
    }

    private void pullJavaEnvImage() {
        ListImagesCmd listImagesCmd = dockerClient.listImagesCmd();
        List<Image> imageList = listImagesCmd.exec();
        
        for (Image image : imageList) {
            String[] repoTags = image.getRepoTags();
            if (repoTags != null && repoTags.length > 0 && sandboxImage.equals(repoTags[0])) {
                return;
            }
        }
        
        PullImageCmd pullImageCmd = dockerClient.pullImageCmd(sandboxImage);
        try {
            pullImageCmd.exec(new PullImageResultCallback()).awaitCompletion();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    //限制资源   控制权限
    private HostConfig getHostConfig(String containerName) {
        HostConfig hostConfig = new HostConfig();
        
        //设置挂载目录，挂载整个代码池目录到容器内
        String codePoolDir = System.getProperty("user.dir") + File.separator + JudgeConstants.CODE_DIR_POOL;
        log.info("挂载配置 - 宿主机目录: {}, 容器内目录: {}", codePoolDir, volumeDir);
        
        // 添加判题结果目录挂载
        String judgeResultsDir = System.getProperty("user.dir") + File.separator + "judge-results";
        log.info("判题结果目录: {}", judgeResultsDir);
        
        // 设置多个挂载点
        hostConfig.setBinds(new Bind[]{
            new Bind(codePoolDir, new Volume(volumeDir)),
            new Bind(judgeResultsDir, new Volume("/judge-results"))
        });
        
        //限制docker容器使用资源
        hostConfig.withMemory(memoryLimit);
        hostConfig.withMemorySwap(memorySwapLimit);
        hostConfig.withCpuCount(cpuLimit);
        hostConfig.withNetworkMode("none");  //禁用网络
        // 移除只读根文件系统限制，允许容器创建必要的用户目录
        // hostConfig.withReadonlyRootfs(true); // 注释掉这行
        return hostConfig;
    }

    public String getCodeDir(String containerId) {
        if (containerId == null || containerId.trim().isEmpty()) {
            log.error("容器ID为空，无法获取代码目录");
            throw new RuntimeException("容器ID为空");
        }
        
        String containerName = containerNameMap.get(containerId);
        log.info("根据容器ID {} 获取容器名称: {}", containerId, containerName);
        
        if (containerName == null || containerName.trim().isEmpty()) {
            log.error("无法找到容器ID {} 对应的容器名称", containerId);
            throw new RuntimeException("无法找到容器名称，容器ID: " + containerId);
        }
        
        String codeDir = System.getProperty("user.dir") + File.separator + JudgeConstants.CODE_DIR_POOL + File.separator + containerName;
        log.info("代码目录路径: {}", codeDir);
        return codeDir;
    }

    //为每个容器，创建的指定挂载文件
    private String createContainerDir(String containerName) {
        //一级目录  存放所有容器的挂载目录
        String codeDir = System.getProperty("user.dir") + File.separator + JudgeConstants.CODE_DIR_POOL;
        if (!FileUtil.exist(codeDir)) {
            FileUtil.mkdir(codeDir);
        }
        return codeDir + File.separator + containerName;
    }
}
